#include "cglm/cglm.h"
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#include "glUtils.h"
#include "initSDL.h"

#define TINYOBJ_LOADER_C_IMPLEMENTATION
#include "tinyobj_loader_c.h"

const uint32_t width = 640, height = 480;

void get_res_path(char *des, const char *name) {
    if (name[0] == '.') {
        name = name + 2;
    }
    sprintf(des, "res/%s", name);
    if (des[strlen(des) - 1] == '\n') {
        des[strlen(des) - 1] = '\0';
    }
}

char *get_file_data(size_t *len, const char *filename) {
    char path[1024];
    get_res_path(path, filename);
    FILE *obj_file = fopen(path, "r");
    if (!obj_file) {
        *len = 0;
        return NULL;
    }
    fseek(obj_file, 0, SEEK_END);
    size_t data_len = ftell(obj_file);
    fseek(obj_file, 0, SEEK_SET);
    char *data = (char *)malloc(data_len);
    memset(data, 0, data_len);
    fread(data, 1, data_len, obj_file);
    fclose(obj_file);
    if (len != NULL) {
        *len = data_len;
    }
    return data;
}

tinyobj_attrib_t attrib;
tinyobj_shape_t *shapes = NULL;
size_t num_shapes;
tinyobj_material_t *materials = NULL;
size_t num_materials;

int LoadObjAndConvert(const char *filename) {
    size_t file_len;

    const char *data = get_file_data(&file_len, filename);
    if (data == NULL) {
        puts("open obj file filed.");
        exit(-1);
    }
    printf("file size: %d\n", (int)file_len);
    {
        unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
        int ret = tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials,
                                    &num_materials, data, file_len, flags,
                                    get_file_data);
        if (ret != TINYOBJ_SUCCESS) {
            exit(-1);
        }
        return 1;
    }
    exit(-1);
}

int main(int argc, char *argv[]) {
    int run = 1;
    char path[128];
    SDL_Window *win = InitSDLWindow("loadobj", width, height);
    LoadObjAndConvert("uvball.obj");
    get_res_path(path, materials->diffuse_texname);
    SDL_Surface *tex = SDL_LoadBMP(path);
    assert(tex != NULL);

    vec3 *vertices = (vec3 *)malloc(sizeof(vec3) * attrib.num_faces);
    vec2 *texcoords = (vec2 *)malloc(sizeof(vec2) * attrib.num_faces);
    GLushort *idx = (GLushort *)malloc(sizeof(GLushort) * attrib.num_faces);

    for (int i = 0; i < attrib.num_faces; i++) {
        tinyobj_vertex_index_t verts = attrib.faces[i];
        idx[i] = i;
        float *p = &attrib.vertices[verts.v_idx * 3];
        vertices[i][0] = p[0];
        vertices[i][1] = p[1];
        vertices[i][2] = p[2];
        float *tp = &attrib.texcoords[verts.vt_idx * 2];
        texcoords[i][0] = tp[0];
        texcoords[i][1] = tp[1];
    }

    glEnable(GL_DEPTH_TEST);
    glClearColor(0.8, 0.8, 0.8, 1.0);

    axis_t axis = InitAxis();
    GLuint program = InitShader("obj");
    assert(program != 0);

    GLint a_pos = glGetAttribLocation(program, "a_Position");
    GLint a_tc = glGetAttribLocation(program, "a_TexCoord");
    GLuint b_verts =
        InitArrayBuffer((float *)vertices, sizeof(vec3) * attrib.num_faces);
    GLuint b_tc =
        InitArrayBuffer((float *)texcoords, sizeof(vec2) * attrib.num_faces);

    GLuint u_tex = InitTexture(program, tex, 0, "u_Sampler");
    GLuint u_mvp = glGetUniformLocation(program, "u_MvpMatrix");
    GLuint idx_buf =
        InitElementsBuffer(idx, sizeof(GLushort) * attrib.num_faces);

    mat4 mvp, m = GLM_MAT4_IDENTITY_INIT, vp;
    {
        mat4 v, p;
        glm_perspective_default((float)width / height, p);
        vec3 eye = {2.0, 2.0, 2.0}, center = {0.0, 0.0, 0.0},
             up = {0.0, 0.0, -1.0};
        glm_lookat(eye, center, up, v);
        glm_mul(p, v, vp);
    }
    glm_mul(vp, m, mvp);
    float angle = 0.0;
    while (run) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                run = 0;
            } else if (event.type == SDL_KEYDOWN) {
                if (event.key.keysym.sym == SDLK_RIGHT) {
                    angle += 0.05;
                } else if (event.key.keysym.sym == SDLK_LEFT) {
                    angle -= 0.05;
                }
                vec3 _axis = {0.0, 0.0, 1.0};
                glm_mat4_identity(m);
                glm_rotate(m, angle, _axis);
                glm_mul(vp, m, mvp);
            }
        }
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(program);
        glUniformMatrix4fv(u_mvp, 1, GL_FALSE, mvp[0]);
        InitAttrBuffer(b_verts, a_pos, 3);
        InitAttrBuffer(b_tc, a_tc, 2);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, u_tex);
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idx_buf);
        glDrawElements(GL_TRIANGLES, attrib.num_faces, GL_UNSIGNED_SHORT, 0);

        DrawAxis(&axis, vp[0]);
        SDL_GL_SwapWindow(win);
    }

    tinyobj_attrib_free(&attrib);
    tinyobj_shapes_free(shapes, num_shapes);
    tinyobj_materials_free(materials, num_materials);
    free(vertices);
    free(texcoords);
    free(idx);
    SDL_FreeSurface(tex);
    SDL_DestroyWindow(win);
    return 0;
}